#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include "p2p-file.h"
#include "p2p-piece.h"
#include "p2p-config.h"
#include "utils.h"
#include "my_log.h"

piece_t *init_piece(uint32_t index)
{
    piece_t *head = (piece_t *)malloc(sizeof(piece_t));

    head->index = index;
    memset(head->block, 0, NO_OF_BLOCKS * sizeof(uint32_t));
    head->prev = NULL;
    head->next = NULL;
    
    return head;
}

int reset_piece(piece_t *piece)
{
    memset(piece->block, 0, NO_OF_BLOCKS * sizeof(uint32_t));
    
    return 0;
}

piece_t *add_piece(piece_t *piece, uint32_t index)
{
    piece_t *temp = (piece_t *)malloc(sizeof(piece_t));
    
    temp->index = index;
    memset(temp->block, 0, NO_OF_BLOCKS * sizeof(uint32_t));
    temp->next = NULL;
    temp->prev = piece;
    piece->next = temp;

    return temp;
}

int del_piece(piece_t **head, piece_t *piece)
{
    assert(piece != NULL);

    if (piece->prev == NULL) {
        *head = piece->next;
        // If there is not just one piece
        if (piece->next != NULL)
            piece->next->prev = NULL;
    } else if (piece->next == NULL) {
        // If there is not just one piece
        if (piece->prev != NULL)
            piece->prev->next = NULL;
    } else {
        piece->prev->next = piece->next;
        piece->next->prev = piece->prev;
    }

    free(piece);
    return 0;
}

int del_piece_all(piece_t **piece_head)
{
    piece_t *temp;

    while (*piece_head != NULL) {
        temp = *piece_head;
        *piece_head = temp->next;
        free(temp);
    }

    *piece_head = NULL;
    return 0;
}

piece_t *search_piece(piece_t *head, uint32_t index)
{
    piece_t *piece;
    
    assert(head != NULL);

    piece = head;
    
    do {
        if (piece->index == index)
            return piece;
        piece = piece->next;
    } while (piece != NULL);

    return NULL;
}

piece_t *select_random_piece(const uint8_t *piece_map, piece_t **head, uint32_t *piece_count)
{
    int i, n;
    piece_t *piece, *temp;

    n = my_rand(*piece_count);

    assert((*head)->prev == NULL);

    // Iterate through piece list
    piece = *head;
    for (i = 0; i < n; i++) {
        piece = piece->next; 
    }
    
    // If we already have the piece, delete it from the list and choose an adjacent piece
    while (piece_map[piece->index]) {
        temp = piece;
        // If the next piece is not NULL choose it
        if (piece->next != NULL) {
            piece = piece->next;
        // else choose the previous piece
        } else if (piece->prev != NULL) {
            piece = piece->prev;
        // If that is also null, we have exhausted the list
        } else {
            return NULL;
        }
        
        LOG(LOG_INFO, "Deleting unwanted piece:%d from peer's piece list", temp->index);
        del_piece(head, temp);
        (*piece_count)--;
    }
 
    LOG(LOG_INFO, "Piece chosen:%d", piece->index);
    return piece;
}

int select_block(const piece_t *piece, block_t *block, int no_of_pieces)
{
    int i, bytes_left, lpbc, lbb;
    
    for (i = 0; i < NO_OF_BLOCKS; i++)
        if (!piece->block[i])
            break;
   
    block->index = piece->index;
    block->begin = i * BLOCK_SIZE;
    
    // Handle last piece
    if (piece->index == no_of_pieces - 1) {
        bytes_left = FILE_SIZE % PIECE_SIZE;
        lbb = bytes_left % BLOCK_SIZE;
        if (lpbc = (bytes_left / BLOCK_SIZE) + !!(lbb)) {
            if (i == lpbc - 1) {
                block->length = lbb;
                return 0;
            } else if (i == lpbc) {
                return 1;
            }
        }
    }

    // handle end of block
    if (i == NO_OF_BLOCKS)
        return 1;

    block->length = BLOCK_SIZE;
    
    return 0;
}
